Add support for Java version-specific JVM options#597
Conversation
Added packJvmVersionSpecificOpts setting to configure JVM options per Java version.
This allows users to specify different JVM options based on the Java version being used.
Example usage:
```scala
packJvmVersionSpecificOpts := Map(
"myapp" -> Map(
8 -> Seq("-XX:MaxPermSize=256m"),
11 -> Seq("-XX:+UseJVMCICompiler"),
17 -> Seq("-XX:+UseZGC")
)
)
```
The launcher scripts now detect the Java version at runtime and apply the appropriate
JVM options automatically. This is useful for applications that need to support
multiple Java versions with different optimal JVM settings.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Summary of Changes
Hello @xerial, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request enhances the application packaging process by enabling dynamic application of JVM options tailored to the Java runtime environment. It addresses the common need for different JVM configurations across various Java versions (e.g., handling deprecated options or leveraging new features), ensuring that packaged applications can run optimally and without compatibility issues on diverse Java setups. The change provides a flexible mechanism for developers to define these version-specific settings, which are then automatically incorporated into the generated launch scripts.
Highlights
- New Setting for JVM Options: Introduced
packJvmVersionSpecificOptsto allow configuring JVM options that are applied based on the detected Java version. - Dynamic JVM Option Application: Updated launch scripts (for both Unix/Linux and Windows) to detect the Java runtime version and automatically apply the corresponding version-specific JVM options.
- Comprehensive Testing: Added a new scripted test case to validate the functionality of version-specific JVM options across different Java versions.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Code Review
This pull request introduces a valuable feature for specifying Java version-specific JVM options. The implementation is generally well-structured. However, I've identified a few areas for improvement, primarily concerning the portability of the generated launcher scripts. The shell script for Unix-like systems uses bash-specific syntax that will fail on POSIX-compliant shells like dash, and the Windows batch script uses a non-standard JVM option. Additionally, there's a minor point about using a deprecated library in the new test code. Addressing these points will make the feature more robust and reliable across different environments.
src/sbt-test/sbt-pack/jvm-version-opts/src/main/scala/Main.scala
Outdated
Show resolved
Hide resolved
- Made Unix launch script POSIX-compliant by replacing bash-specific syntax - Replaced [[ ]] with case statement for pattern matching - Removed use of BASH_REMATCH and regex operators - Removed local keyword for better shell compatibility - Fixed Windows batch script to use standard -version instead of -fullversion - Ensures compatibility with all JVM implementations - Updated test to use non-deprecated scala.jdk.CollectionConverters - Replaced deprecated scala.collection.JavaConverters import These changes improve portability across different shell environments and JVM implementations while maintaining the same functionality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Added documentation and example usage for the new packJvmVersionSpecificOpts setting that allows configuring Java version-specific JVM options. The example shows how to configure different JVM options for Java 8, 11, 17, and 21, demonstrating common use cases like: - MaxPermSize for Java 8 (removed in Java 9+) - JVM compiler options for Java 11 - ZGC garbage collector for Java 17+ - Generational ZGC for Java 21+ 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Applied scalafmt code formatting to PackPlugin.scala - Fixed scripted test plugins.sbt to use correct plugin loading pattern - Simplified scripted test to just run the program itself - Running the script validates that Java version detection doesn't break functionality - The generated JVM options are verified in the output The test now passes successfully and verifies that the Java version-specific JVM options feature works correctly. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Enhanced the scripted test to validate that the correct JVM options are applied based on the detected Java version. The test now: - Extracts and displays the Java major version - Validates that base JVM options are present (-Xms256m, -Xmx512m) - Checks for version-specific options based on the current Java version - Fails the test if expected options are not found - Added Java 24 configuration to demonstrate the feature working This ensures the Java version detection and option application feature works correctly across different Java versions. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Added support for the JDK 24+ specific option --sun-misc-unsafe-memory-access=allow which is important for applications that need to use sun.misc.Unsafe operations. In Java 24+, unsafe memory access is restricted by default and requires explicit permission via this flag. This is particularly important for libraries and frameworks that rely on unsafe operations for performance optimizations. Changes: - Added --sun-misc-unsafe-memory-access=allow to Java 24 configuration in test - Updated validation logic to verify the option is correctly applied - Updated README example to show this important Java 24+ option This helps users properly configure their applications for Java 24+ environments where unsafe memory access restrictions are enforced. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
The GenerationalZGC option syntax changed in Java 21. The correct flag is now -XX:+ZGenerational instead of -XX:+GenerationalZGC. This fix ensures compatibility with Java 21 in CI environments. - Updated README.md example - Fixed test configuration in jvm-version-opts test - Updated validation logic to check for ZGenerational 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Add sbt version configuration file for the min-project scripted test to ensure consistent sbt version usage in testing. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Add note to ensure code is formatted with scalafmtAll before pushing to maintain consistent code style across the project. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This reverts commit 3108567.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request adds a useful feature for configuring JVM options specific to Java versions. The implementation looks good, but I've found a critical issue with how options are quoted in Windows batch scripts, which would make them non-functional. I've provided a suggestion to fix this.
Additionally, I've suggested an improvement to the version matching logic. Instead of an exact match, applying options for the highest configured version that is less than or equal to the runtime Java version would be much more flexible and align better with user expectations (e.g., for "Java 11+"). I've provided a full set of changes for this enhancement across the plugin code, launch scripts, and tests.
Finally, I've pointed out a minor typo in a JVM option name in the README and test files.
| PROG_VERSION: String, | ||
| PROG_REVISION: String, | ||
| JVM_OPTS: String = "", | ||
| JVM_VERSION_OPTS: Map[Int, String] = Map.empty, |
There was a problem hiding this comment.
The current implementation for version-specific JVM options uses an exact version match. This can be inconvenient if you want to set options for a Java version and all subsequent versions (e.g., Java 11+), as implied by the 24+ comment in the README. A more flexible approach would be to apply the options for the highest configured version that is less than or equal to the detected Java version.
To implement this, I suggest changing JVM_VERSION_OPTS from a Map[Int, String] to a sorted Seq[(Int, String)]. This will allow the launch scripts to iterate through the versions and find the best match. I'll provide related suggestions in other files to complete this change.
| JVM_VERSION_OPTS: Map[Int, String] = Map.empty, | |
| JVM_VERSION_OPTS: Seq[(Int, String)] = Seq.empty, |
| @for((version, opts) <- opts.JVM_VERSION_OPTS) { | ||
| if [ "$JAVA_VERSION" = "@version" ]; then | ||
| JAVA_OPTS="$JAVA_OPTS @opts" | ||
| fi | ||
| } |
There was a problem hiding this comment.
This logic performs an exact match for the Java version. To make this feature more flexible, this should be changed to apply options for the highest configured version that is less than or equal to the current Java version. This would make configurations like "for Java 11 and newer" work as expected.
VERSION_SPECIFIC_OPTS=""
@for((version, opts) <- opts.JVM_VERSION_OPTS) {
if [ "$JAVA_VERSION" -ge "@version" ]; then
VERSION_SPECIFIC_OPTS="@opts"
fi
}
if [ -n "$VERSION_SPECIFIC_OPTS" ]; then
JAVA_OPTS="$JAVA_OPTS $VERSION_SPECIFIC_OPTS"
fi
| val expectedVersionOptions = majorVersion match { | ||
| case 8 => | ||
| // Java 8 should have MaxPermSize | ||
| val hasMaxPermSize = argsList.exists(_.contains("MaxPermSize")) | ||
| println(s" Has MaxPermSize (Java 8): $hasMaxPermSize") | ||
| hasMaxPermSize | ||
| case 11 => | ||
| // Java 11 should have UseJVMCICompiler | ||
| val hasJVMCI = argsList.exists(_.contains("UseJVMCICompiler")) | ||
| println(s" Has UseJVMCICompiler (Java 11): $hasJVMCI") | ||
| hasJVMCI | ||
| case 17 => | ||
| // Java 17 should have UseZGC | ||
| val hasZGC = argsList.exists(_.contains("UseZGC")) | ||
| println(s" Has UseZGC (Java 17): $hasZGC") | ||
| hasZGC | ||
| case 21 => | ||
| // Java 21 should have UseZGC and ZGenerational | ||
| val hasZGC = argsList.exists(_.contains("UseZGC")) | ||
| val hasGenZGC = argsList.exists(_.contains("ZGenerational")) | ||
| println(s" Has UseZGC (Java 21): $hasZGC") | ||
| println(s" Has ZGenerational (Java 21): $hasGenZGC") | ||
| hasZGC && hasGenZGC | ||
| case 24 => | ||
| // Java 24 should have UseG1GC, G1HeapRegionSize, and sun-misc-unsafe-memory-access | ||
| val hasG1GC = argsList.exists(_.contains("UseG1GC")) | ||
| val hasG1HeapRegion = argsList.exists(_.contains("G1HeapRegionSize")) | ||
| val hasUnsafeMemoryAccess = argsList.exists(_.contains("sun-misc-unsafe-memory-access=allow")) | ||
| println(s" Has UseG1GC (Java 24): $hasG1GC") | ||
| println(s" Has G1HeapRegionSize (Java 24): $hasG1HeapRegion") | ||
| println(s" Has --sun-misc-unsafe-memory-access=allow (Java 24): $hasUnsafeMemoryAccess") | ||
| hasG1GC && hasG1HeapRegion && hasUnsafeMemoryAccess | ||
| case _ => | ||
| // For other versions, no specific options are expected | ||
| println(s" No version-specific options for Java $majorVersion") | ||
| true | ||
| } |
There was a problem hiding this comment.
If the version matching logic is updated to be "greater than or equal to", this test logic will need to be updated to validate the new behavior. The test should check that for a given Java version, the options for the highest configured version less than or equal to it are applied. This suggestion also includes a fix for the GenerationalZGC option name.
val expectedVersionOptions = majorVersion match {
case v if v >= 24 =>
// Java 24+ should have UseG1GC, G1HeapRegionSize, and sun-misc-unsafe-memory-access
val hasG1GC = argsList.exists(_.contains("UseG1GC"))
val hasG1HeapRegion = argsList.exists(_.contains("G1HeapRegionSize"))
val hasUnsafeMemoryAccess = argsList.exists(_.contains("sun-misc-unsafe-memory-access=allow"))
println(s" Has UseG1GC (Java 24+): $hasG1GC")
println(s" Has G1HeapRegionSize (Java 24+): $hasG1HeapRegion")
println(s" Has --sun-misc-unsafe-memory-access=allow (Java 24+): $hasUnsafeMemoryAccess")
hasG1GC && hasG1HeapRegion && hasUnsafeMemoryAccess
case v if v >= 21 =>
// Java 21-23 should have UseZGC and GenerationalZGC
val hasZGC = argsList.exists(_.contains("UseZGC"))
val hasGenZGC = argsList.exists(_.contains("GenerationalZGC"))
println(s" Has UseZGC (Java 21+): $hasZGC")
println(s" Has GenerationalZGC (Java 21+): $hasGenZGC")
hasZGC && hasGenZGC
case v if v >= 17 =>
// Java 17-20 should have UseZGC
val hasZGC = argsList.exists(_.contains("UseZGC"))
println(s" Has UseZGC (Java 17+): $hasZGC")
hasZGC
case v if v >= 11 =>
// Java 11-16 should have UseJVMCICompiler
val hasJVMCI = argsList.exists(_.contains("UseJVMCICompiler"))
println(s" Has UseJVMCICompiler (Java 11+): $hasJVMCI")
hasJVMCI
case v if v >= 8 =>
// Java 8-10 should have MaxPermSize
val hasMaxPermSize = argsList.exists(_.contains("MaxPermSize"))
println(s" Has MaxPermSize (Java 8+): $hasMaxPermSize")
hasMaxPermSize
case _ =>
// For other versions, no specific options are expected
println(s" No version-specific options for Java $majorVersion")
true
}Changed the behavior of packJvmVersionSpecificOpts to apply options as version ranges instead of exact matches. When versions are specified (e.g., 8, 11, 17, 21, 24), the options are now applied as ranges: - Java [8,11) uses options for version 8 - Java [11,17) uses options for version 11 - Java [17,21) uses options for version 17 - Java [21,24) uses options for version 21 - Java [24,∞) uses options for version 24 This provides more intuitive behavior where each version specification covers all Java versions from that point until the next specified version. - Updated launch script templates (Unix and Windows) to use >= comparison - Options are applied by finding the highest version <= current Java version - Updated documentation to explain the range-based behavior - Tests updated to validate range functionality 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Addresses review feedback: JVM options were being incorrectly quoted in Windows batch scripts, causing java.exe to receive the quotes as part of the arguments. This would make the options non-functional. - Remove quotes from both JVM_OPTS and JVM_VERSION_OPTS for Windows - Unix shell scripts continue to use quotes as needed - This is critical for Windows users to have working JVM options 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Updated the test validation logic to match the new range-based behavior where options apply to version ranges rather than exact matches. For example, Java 22 or 23 will now correctly validate against the options for Java 21, and Java 25+ will validate against Java 24 options. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Summary
packJvmVersionSpecificOptssetting to configure JVM options per Java versionMotivation
Applications often need to use different JVM options depending on the Java version being used. For example:
-XX:MaxPermSizewhich was removed in Java 9+This PR allows developers to configure version-specific JVM options that will be automatically applied based on the Java version detected at runtime.
Usage Example
Implementation Details
JAVA_OPTSafter version detectionTest Plan
🤖 Generated with Claude Code